#include "lambda_tuple.h"

int lambda_capture() {
    // 1. 省略了opt的名字
    auto f = [](int a) -> int { return a + 1; };
    f(1);

    // 2. 自动推导返回值类型
    auto g = [](int a) { return a + 1; };
    g(1);

    // 3. 没有参数时，参数的格式可以省略
    auto h = [] { return 1; };


    // 4. labmda 捕获方式
    A obj;
    obj.func(2, 3);

    // TODO: 特别提醒
    // 5. 按值捕获的方式实在lambda表达式定义的时候捕获的而不是执行的时候
    int a = 0;
    auto i = [=] { return a; };

    a += 1;
    // 输出为0， 因为捕获发生在lambda定义时
    // 所以推荐按引用的方式捕获外部变量
    std::cout << i() << std::endl;    

    // 6. 修改按值捕获的外部变量
    // TODO: 被mutable修饰的lambda即使没有参数，()也必须要留着
    // TODO: class中被mutable修饰的成员变量可以被修饰为const的成员方法修改
    //       lambda表达式可以看做有operator()的类，其默认也是被const修饰的
    //       按值捕获的外部变量成为了类的成员变量, 因此使用mutable修饰时可以修改
    //       按值捕获的外部变量!!!
    int val = 0;
    auto j = [=] () mutable { return val++; };

    // 7. 绑定
    std::function<int(int)> f1  = [] (int a) { return a; };
    std::function<int(void)> f2 = std::bind([] (int a) { return a; }, 123);


    // TODO: 8  没有捕获任何变量的表达式可以转化为函数指针
    using func_t = int(*) (int);
    func_t    f3 = [] (int a) { return a; };
    f3(256); 

    // 9. 声明式的编程风格
    std::vector<int> v{1,2,3,4,5,6,7,8,9};
    int even_count = 0;
    std::for_each(v.begin(), v.end(), [&even_count](int val) {
        if (val % 2 == 0) {
            ++even_count;
        }
    }); // for_each

    // 统计(5, 10)之间的值的数量
    int count_gt5 = std::count_if(v.begin(), v.end(), [](int val) {
        return val > 5 && val < 10;
    });


    return 0;
}

int std_tuple() {
    // 1. 替换结构体
    // struct Student {
    //     const char *name;
    //     int age;
    // };
    
    int idx          = 1;
    std::string name = "yangxian";
    int   age        = 18;
    auto  tp1        = std::tuple<std::string, int>(name, age);

    // 2. std::tie 创建一个元组的左值引用
    // tp2的实际类型是: std::tuple<int&, std::string&, int&>
    // tie创建的是左值引用, 因此传入的值必须是左值
    auto  tp2   = std::tie(idx, name, age);

    // 3. std::get 获取元组中的值
    std::cout << std::get<0>(tp2) << std::endl;
    std::cout << std::get<1>(tp2) << std::endl;
    std::cout << std::get<2>(tp2) << std::endl;

    // 4. std::tuple_size 获取元组的大小, 即元组中元素的数量
    std::cout << std::tuple_size<decltype(tp2)>::value << std::endl;

    // 5. std::tuple_element 获取元组中的元素类型
    // std::tuple_element<index, TupleType>::type
    // using type0 = std::tuple_element<0,   decltype(tp2)::type;
    using type0 = std::tuple_element<0, decltype(tp2)>::type;
    using type1 = std::tuple_element<1, decltype(tp2)>::type;
    static_assert(std::is_same<type0, int&>::value,         "first type is not int&");
    static_assert(std::is_same<type1, std::string&>::value, "second type is not std::string&");

    // // 7. 元组解包
    std::tie(idx, name, age) = tp2;                   // 将tp1的内部值解包到离散的变量
    std::tie(std::ignore, std::ignore, age) = tp2;    // 不解包所有的变量

    // 8. tuple拼接
    auto tp3 = std::tuple_cat(tp1, tp2, std::make_pair("Foo", "bar"));
    std::cout << std::tuple_size<decltype(tp3)>::value << std::endl;

    return 0;
}